// ==UserScript==
// @name        ModSearch
// @namespace   PMODSEARCH
// @description Modifies class pages on Piazza to allow for multiclass searching.
// @include     https://piazza.com/class*
// @version     1
// @grant		none
// ==/UserScript==

DEBUG = 1
INACTIVE_NETWORK_STRING = 'inactiveNetwork'

//
// Given an expression to match, returns an array of network IDs whose class names match the expression.
//
var getNetworkIds = function( pattern, classArrayData ) {
	var result = new Array( );
	var regExpr = new RegExp( pattern, 'i' );
	for ( var i = 0; i < classArrayData.length; i += 2 ) {
		if ( regExpr.test( classArrayData[i] ) == true ) {
			result.push( classArrayData[i + 1] );
		}
	}

	return result;
}

//
// Returns the current user's authorization token.
// Returns null on error.
//
var getAuthorizationToken = function( ) {
	var authToken = null;
	if ( typeof PA != 'undefined' && typeof PA.apiCache != 'undefined' && typeof PA.apiCache['user.status{}'] != 'undefined' && typeof PA.apiCache['user.status{}'].aid != 'undefined') {
		authToken = PA.apiCache['user.status{}'].aid;
	}
	return authToken;
}

//
// Returns the network Id as a string from the given raw network string.
//
var getNetworkId = function( rawNetworkId ) {
	var capExpr = /^\s*network_(.+)\s*$/;
	var match = capExpr.exec( rawNetworkId );
	return match[1];
}

//
// Returns the given string stripped of nonalphanumeric characters.
//
var cleanNonAlphanumeric = function( rawString ) {
	return rawString.replace( /\W/g, '' );
}

//
// Takes a class name and puts it into a specific format:
//		decoded HTML
//		lowercase
//		nonalphanumeric characters stripped
// Assumes that a class name is given
//
var cleanClassName = function( rawClassName ) {
	// decode HTML
	var decodeWorkspace = document.createElement( 'textarea' );
	decodeWorkspace.innerHTML = rawClassName;
	var decodedClassName = decodeWorkspace.value;
	
	var lowerCase = decodedClassName.toLowerCase( );
	var stripped = cleanNonAlphanumeric( lowerCase );
	return stripped;
}

//
// Returns an array of the user's classes.
// Only all active classes are returned in this array iff active is true.
// Returns an empty list on error.
// Elements returned are strings, where for an even i, 
//	return[i] is a class name and return[i+1] is the network id, both as strings.
//
var getClasses = function( active ) {
	// return value
	var myClasses = new Array( );
	// root element for classes list (found in the drop down menu)
	var myClassesRoot = document.getElementById( 'myClasses' );
	if ( myClassesRoot != null ) {
		// get children, which will be LI elements
		var myClassesLIs = myClassesRoot.childNodes;
		if ( myClassesLIs != null ) {
		// loop over children
		for ( var i = 1; i < myClassesLIs.length; i++ ) {
			var currentClassLI = myClassesLIs[i];

			// filter out unwanted networks (if applicable)
			if ( (active == true && currentClassLI.className.indexOf( INACTIVE_NETWORK_STRING ) == -1) || (active == false) ) {
				var rawNetworkId = currentClassLI.id;
				var rawClassName = currentClassLI.childNodes[0].childNodes[0].innerHTML;
				
				// clean class name
				var className = cleanClassName( rawClassName );
				// clean network Id
				var networkId = getNetworkId( rawNetworkId );
				myClasses.push( className );
				myClasses.push( networkId );
			}
		}}
	}
	return myClasses;
}

var addFeed = function( feedItems ) { 
	PD.feedFilter = null;
	PD.search_feed = feedItems;
	if ( PD.searchSort === 'date' ) {
		PD.search_feed.sort( function(a,b) {
			if ( b.updated > a.updated ) return 1;
			if ( b.updated < a.updated ) return -1;
			return 0;
		} );
		PD.addBuckets( PD.search_feed );
	} else {
		PD.addSearchBuckets(PD.search_feed);
	}
	PD.createFeed( PD.search_feed );
}

var innerQuery = function( result ) {
	// add current results
	Array.prototype.push.apply( PD.search_feed, result );

	// append next results
	var inpQuery = sessionStorage.getItem( 'inpQuery' );
	var networks = sessionStorage.getItem( 'classes' );

	var currentNetwork = null;
	var laterNetworks = null;

	var firstSplit = networks.indexOf(',');
	if ( firstSplit == -1 ) {
		if ( networks.length != 0 ) { // last network
			currentNetwork = networks;
			laterNetworks = '';
		} else { // EOD
			// alert( PD.search_feed.length );
			// apply search
			if ( PD.searchSort === 'date' ) {
			PD.search_feed.sort( function(a,b) {
				if ( b.updated > a.updated ) return 1;
				if ( b.updated < a.updated ) return -1;
				return 0;
				} );
				PD.addBuckets( PD.search_feed );
			} else {
				PD.addSearchBuckets(PD.search_feed);
			}
			PD.createFeed( PD.search_feed );
			return;
		}
	} else {
		currentNetwork = networks.slice( 0, firstSplit );
		laterNetworks = networks.slice( firstSplit + 1, networks.length );
	}

	sessionStorage.setItem( 'classes', laterNetworks );
	PA.call_pj( 'network.search', { nid:currentNetwork, query: inpQuery },  $('#question-feed-questions'), innerQuery, function(err) { } );
}

//
// Performs a search to all networks whose class name matches the expression, with the given query.
// Cascades the search requests using sessionStorage, and when EOD is reached it posts the results to the feed.
//
var queryClasses = function( classExpr, inpQuery ) {
	var targetNetworks = getNetworkIds( classExpr, getClasses( true ) );

	// used to store results
	PD.search_feed = new Array( );

	// setup to linearize queries and prevent clobbering of data
	sessionStorage.setItem( 'inpQuery', inpQuery );
	sessionStorage.setItem( 'classes', targetNetworks.join( ) );

	innerQuery( new Array( ) );
}

// queries through textbox given:
// classExpr:searchText
var onTestButtonSubmit = function( event ) {
	// var feedItems = queryClasses( 'cs161|cs170', '#pin' ); // used for initial testing
	var customQueryTextArea = document.getElementById( 'myTextArea' );
	if ( customQueryTextArea != null ) {
		var queryString = customQueryTextArea.value;
		var splitter = queryString.lastIndexOf( ':' );
		if ( splitter != -1 && splitter != (queryString.length - 1) ) { // nonempty search string
			var classExpr = queryString.slice( 0, splitter );
			var query = queryString.slice( splitter + 1, queryString.length );
			// perform the query
			queryClasses( classExpr, query );
		}
	}
}

// initialization i needed
myTestButton = document.getElementById( 'myTestButton' );
if ( myTestButton == null && DEBUG == 1) {
	feedSearch = document.getElementById( 'FeedSearch' );
	if ( feedSearch != null ) {
		myTestButton = document.createElement( 'input' );
		myTestButton.setAttribute( 'type','submit' );
		myTestButton.setAttribute( 'value',' DEBUG ' );
		myTestButton.setAttribute( 'id','myTestButton' );
		myTestForm = document.createElement( 'form' );
		myTestForm.setAttribute( 'id', 'myTestForm' );
		// may need to change per person, should find a different place to put this
		feedSearch.appendChild( document.createElement( 'br' ) );
		feedSearch.appendChild( document.createElement( 'br' ) );
		myTestForm.appendChild( myTestButton );

		myTextArea = document.createElement( 'textarea' );
		myTextArea.setAttribute( 'rows', 1 );
		myTextArea.setAttribute( 'cols', 12 );
		myTextArea.setAttribute( 'id', 'myTextArea' );
		myTestForm.appendChild( myTextArea );

		feedSearch.appendChild( myTestForm );
		myTestForm.addEventListener('submit',onTestButtonSubmit);
	}
}